home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / timidsrc.zip / linux_a.c < prev    next >
C/C++ Source or Header  |  1996-05-20  |  6KB  |  230 lines

  1. /* 
  2.  
  3.     TiMidity -- Experimental MIDI to WAVE converter
  4.     Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20.     linux_audio.c
  21.  
  22.     Functions to play sound on the VoxWare audio driver (Linux or FreeBSD)
  23.  
  24. */
  25.  
  26. #include <unistd.h>
  27. #include <fcntl.h>
  28. #include <errno.h>
  29.  
  30. #ifdef linux
  31. #include <sys/ioctl.h> /* new with 1.2.0? Didn't need this under 1.1.64 */
  32. #include <linux/soundcard.h>
  33. #endif
  34.  
  35. #ifdef __FreeBSD__
  36. #include <stdio.h>
  37. #include <machine/soundcard.h>
  38. #endif
  39.  
  40. #include "config.h"
  41. #include "output.h"
  42. #include "controls.h"
  43.  
  44. static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
  45. static void close_output(void);
  46. static void output_data(int32 *buf, int32 count);
  47. static void flush_output(void);
  48. static void purge_output(void);
  49.  
  50. /* export the playback mode */
  51.  
  52. #define dpm linux_play_mode
  53.  
  54. PlayMode dpm = {
  55.   DEFAULT_RATE, PE_16BIT|PE_SIGNED,
  56.   -1,
  57.   {0}, /* default: get all the buffer fragments you can */
  58.   "Linux dsp device", 'd',
  59.   "/dev/dsp",
  60.   open_output,
  61.   close_output,
  62.   output_data,
  63.   flush_output,
  64.   purge_output  
  65. };
  66.  
  67.  
  68. /*************************************************************************/
  69. /* We currently only honor the PE_MONO bit, the sample rate, and the
  70.    number of buffer fragments. We try 16-bit signed data first, and
  71.    then 8-bit unsigned if it fails. If you have a sound device that
  72.    can't handle either, let me know. */
  73.  
  74. static int open_output(void)
  75. {
  76.   int fd, tmp, i, warnings=0;
  77.   
  78.   /* Open the audio device */
  79.   fd=open(dpm.name, O_RDWR | O_NDELAY);
  80.   if (fd<0)
  81.     {
  82.       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
  83.        dpm.name, sys_errlist[errno]);
  84.       return -1;
  85.     }
  86.  
  87.   /* They can't mean these */
  88.   dpm.encoding &= ~(PE_ULAW|PE_BYTESWAP);
  89.  
  90.  
  91.   /* Set sample width to whichever the user wants. If it fails, try
  92.      the other one. */
  93.  
  94.   i=tmp=(dpm.encoding & PE_16BIT) ? 16 : 8;
  95.   if (ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &tmp)<0 || tmp!=i)
  96.     {
  97.       /* Try the other one */
  98.       i=tmp=(dpm.encoding & PE_16BIT) ? 8 : 16;
  99.       if (ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &tmp)<0 || tmp!=i)
  100.     {
  101.       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, 
  102.             "%s doesn't support 16- or 8-bit sample width", 
  103.             dpm.name);
  104.       close(fd);
  105.       return -1;
  106.     }
  107.       ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
  108.         "Sample width adjusted to %d bits", tmp);
  109.       dpm.encoding ^= PE_16BIT;
  110.       warnings=1;
  111.     }
  112.   if (dpm.encoding & PE_16BIT)
  113.     dpm.encoding |= PE_SIGNED;
  114.   else
  115.     dpm.encoding &= ~PE_SIGNED;
  116.  
  117.  
  118.   /* Try stereo or mono, whichever the user wants. If it fails, try
  119.      the other. */
  120.  
  121.   i=tmp=(dpm.encoding & PE_MONO) ? 0 : 1;
  122.   if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp)<0) || tmp!=i)
  123.     {
  124.       i=tmp=(dpm.encoding & PE_MONO) ? 1 : 0;
  125.  
  126.       if ((ioctl(fd, SNDCTL_DSP_STEREO, &tmp)<0) || tmp!=i)
  127.     {
  128.       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, 
  129.            "%s doesn't support mono or stereo samples",
  130.            dpm.name);
  131.       close(fd);
  132.       return -1;
  133.     }
  134.       if (tmp==0) dpm.encoding |= PE_MONO;
  135.       else dpm.encoding &= ~PE_MONO;
  136.       ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Sound adjusted to %sphonic", 
  137.        (tmp==0) ? "mono" : "stereo");
  138.       warnings=1;
  139.     }
  140.  
  141.  
  142.   /* Set the sample rate */
  143.   
  144.   tmp=dpm.rate;
  145.   if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp)<0)
  146.     {
  147.       ctl->cmsg(CMSG_ERROR, VERB_NORMAL, 
  148.        "%s doesn't support a %d Hz sample rate",
  149.        dpm.name, dpm.rate);
  150.       close(fd);
  151.       return -1;      
  152.     }
  153.   if (tmp != dpm.rate)
  154.     {
  155.       dpm.rate=tmp;
  156.       ctl->cmsg(CMSG_WARNING, VERB_VERBOSE,
  157.        "Output rate adjusted to %d Hz", dpm.rate);
  158.       warnings=1;
  159.     }
  160.  
  161.   /* Older VoxWare drivers don't have buffer fragment capabilities */  
  162. #ifdef SNDCTL_DSP_SETFRAGMENT
  163.   /* Set buffer fragments (in extra_param[0]) */
  164.   
  165.   tmp=AUDIO_BUFFER_BITS;
  166.   if (!(dpm.encoding & PE_MONO)) tmp++;
  167.   if (dpm.encoding & PE_16BIT) tmp++;
  168.   tmp |= (dpm.extra_param[0]<<16);
  169.   i=tmp;
  170.   if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &tmp)<0)
  171.     {
  172.       ctl->cmsg(CMSG_WARNING, VERB_NORMAL, 
  173.        "%s doesn't support %d-byte buffer fragments", dpm.name, (1<<i));
  174.       /* It should still work in some fashion. We should use a
  175.      secondary buffer anyway -- 64k isn't enough. */
  176.       warnings=1;
  177.     }
  178. #else
  179.   if (dpm.extra_param[0])
  180.     {
  181.       ctl->cmsg(CMSG_WARNING, VERB_NORMAL, 
  182.         "%s doesn't support buffer fragments", dpm.name);
  183.       warnings=1;
  184.     }
  185. #endif
  186.  
  187.   dpm.fd=fd;
  188.   
  189.   return warnings;
  190. }
  191.  
  192. static void output_data(int32 *buf, int32 count)
  193. {
  194.   if (!(dpm.encoding & PE_MONO)) count*=2; /* Stereo samples */
  195.   
  196.   if (dpm.encoding & PE_16BIT)
  197.     {
  198.       /* Convert data to signed 16-bit PCM */
  199.       s32tos16(buf, count);
  200.       
  201.       /* Write the data out. Linux likes to give an EINTR if you suspend
  202.      a program while waiting on a write, so we may need to retry. */
  203.       while ((-1==write(dpm.fd, buf, count * 2)) && errno==EINTR)
  204.     ;
  205.     }
  206.   else
  207.     {
  208.       /* Convert to 8-bit unsigned and write out. */
  209.       s32tou8(buf, count);
  210.       
  211.       while ((-1==write(dpm.fd, buf, count)) && errno==EINTR)
  212.     ;
  213.     }
  214. }
  215.  
  216. static void close_output(void)
  217. {
  218.   close(dpm.fd);
  219. }
  220.  
  221. static void flush_output(void)
  222. {
  223.   ioctl(dpm.fd, SNDCTL_DSP_SYNC);
  224. }
  225.  
  226. static void purge_output(void)
  227. {
  228.   ioctl(dpm.fd, SNDCTL_DSP_RESET);
  229. }
  230.